Condividi tramite


Struct System.Span<T>

Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.

Il Span<T> tipo è uno struct di riferimento allocato nello stack anziché nell'heap gestito. I tipi di struct ref hanno una serie di restrizioni per assicurarsi che non possano essere promossi all'heap gestito, tra cui che non possono essere boxed, non possono essere assegnati a variabili di tipo Objectdynamic o a qualsiasi tipo di interfaccia, non possono essere campi in un tipo di riferimento e non possono essere usati oltre await i limiti e yield . Inoltre, chiama due metodi e Equals(Object) GetHashCodegenera un'eccezione NotSupportedException.

Importante

Poiché si tratta di un tipo solo stack, Span<T> non è adatto per molti scenari che richiedono l'archiviazione di riferimenti ai buffer nell'heap. Questo vale, ad esempio, per le routine che effettuano chiamate asincrone al metodo. Per questi scenari, è possibile usare i tipi e System.ReadOnlyMemory<T> complementariSystem.Memory<T>.

Per gli intervalli che rappresentano strutture non modificabili o di sola lettura, usare System.ReadOnlySpan<T>.

Memoria

Un Span<T> oggetto rappresenta un'area contigua di memoria arbitraria. Un'istanza Span<T> viene spesso utilizzata per contenere gli elementi di una matrice o una parte di una matrice. A differenza di una matrice, tuttavia, un'istanza Span<T> può puntare alla memoria gestita, alla memoria nativa o alla memoria gestita nello stack. L'esempio seguente crea un oggetto Span<Byte> da una matrice:

// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(array);

byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
    arraySpan[ctr] = data++;

int arraySum = 0;
foreach (var value in array)
    arraySum += value;

Console.WriteLine($"The sum is {arraySum}");
// Output:  The sum is 4950
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array

let mutable data = 0uy
for i = 0 to arraySpan.Length - 1 do
    arraySpan[i] <- data
    data <- data + 1uy

let mutable arraySum = 0
for value in array do
    arraySum <- arraySum + int value

printfn $"The sum is {arraySum}"
// Output:  The sum is 4950

Nell'esempio seguente viene creato un oggetto Span<Byte> da 100 byte di memoria nativa:

// Create a span from native memory.
var native = Marshal.AllocHGlobal(100);
Span<byte> nativeSpan;
unsafe
{
    nativeSpan = new Span<byte>(native.ToPointer(), 100);
}
byte data = 0;
for (int ctr = 0; ctr < nativeSpan.Length; ctr++)
    nativeSpan[ctr] = data++;

int nativeSum = 0;
foreach (var value in nativeSpan)
    nativeSum += value;

Console.WriteLine($"The sum is {nativeSum}");
Marshal.FreeHGlobal(native);
// Output:  The sum is 4950
// Create a span from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)

let mutable data = 0uy
for i = 0 to nativeSpan.Length - 1 do
    nativeSpan[i] <- data
    data <- data + 1uy

let mutable nativeSum = 0
for value in nativeSpan do
    nativeSum <- nativeSum + int value

printfn $"The sum is {nativeSum}"
Marshal.FreeHGlobal native
// Output:  The sum is 4950

L'esempio seguente usa la parola chiave stackalloc C# per allocare 100 byte di memoria nello stack:

// Create a span on the stack.
byte data = 0;
Span<byte> stackSpan = stackalloc byte[100];
for (int ctr = 0; ctr < stackSpan.Length; ctr++)
    stackSpan[ctr] = data++;

int stackSum = 0;
foreach (var value in stackSpan)
    stackSum += value;

Console.WriteLine($"The sum is {stackSum}");
// Output:  The sum is 4950
    // Create a span on the stack.
    let mutable data = 0uy
    let stackSpan = 
        let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
        Span<byte>(p, 100)

    for i = 0 to stackSpan.Length - 1 do
        stackSpan[i] <- data
        data <- data + 1uy

    let mutable stackSum = 0
    for value in stackSpan do
        stackSum <- stackSum + int value

    printfn $"The sum is {stackSum}"
// Output:  The sum is 4950

Poiché Span<T> è un'astrazione su un blocco arbitrario di memoria, i metodi del tipo e dei Span<T> metodi con Span<T> parametri operano su qualsiasi Span<T> oggetto indipendentemente dal tipo di memoria incapsulato. Ad esempio, ognuna delle sezioni separate del codice che inizializza l'intervallo e calcola la somma dei relativi elementi può essere modificata in singoli metodi di inizializzazione e calcolo, come illustrato nell'esempio seguente:

public static void WorkWithSpans()
{
    // Create a span over an array.
    var array = new byte[100];
    var arraySpan = new Span<byte>(array);

    InitializeSpan(arraySpan);
    Console.WriteLine($"The sum is {ComputeSum(arraySpan):N0}");

    // Create an array from native memory.
    var native = Marshal.AllocHGlobal(100);
    Span<byte> nativeSpan;
    unsafe
    {
        nativeSpan = new Span<byte>(native.ToPointer(), 100);
    }

    InitializeSpan(nativeSpan);
    Console.WriteLine($"The sum is {ComputeSum(nativeSpan):N0}");

    Marshal.FreeHGlobal(native);

    // Create a span on the stack.
    Span<byte> stackSpan = stackalloc byte[100];

    InitializeSpan(stackSpan);
    Console.WriteLine($"The sum is {ComputeSum(stackSpan):N0}");
}

public static void InitializeSpan(Span<byte> span)
{
    byte value = 0;
    for (int ctr = 0; ctr < span.Length; ctr++)
        span[ctr] = value++;
}

public static int ComputeSum(Span<byte> span)
{
    int sum = 0;
    foreach (var value in span)
        sum += value;

    return sum;
}
// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950
open System
open System.Runtime.InteropServices
open FSharp.NativeInterop

// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
    let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
    Span<'a>(voidPointer, length)

let initializeSpan (span: Span<byte>) =
    let mutable value = 0uy
    for i = 0 to span.Length - 1 do
        span[i] <- value
        value <- value + 1uy

let computeSum (span: Span<byte>) =
    let mutable sum = 0
    for value in span do
        sum <- sum + int value
    sum

let workWithSpans () =
    // Create a span over an array.
    let array = Array.zeroCreate<byte> 100
    let arraySpan = Span<byte> array

    initializeSpan arraySpan
    printfn $"The sum is {computeSum arraySpan:N0}"

    // Create an array from native memory.
    let native = Marshal.AllocHGlobal 100
    let nativeSpan = Span<byte>(native.ToPointer(), 100)

    initializeSpan nativeSpan
    printfn $"The sum is {computeSum nativeSpan:N0}"

    Marshal.FreeHGlobal native

    // Create a span on the stack.
    let stackSpan = stackalloc 100

    initializeSpan stackSpan
    printfn $"The sum is {computeSum stackSpan:N0}"

// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950

Matrici

Quando esegue il wrapping di una matrice, Span<T> può eseguire il wrapping di un'intera matrice, come negli esempi nella sezione Memoria . Poiché supporta il sezionamento, Span<T> può anche puntare a qualsiasi intervallo contiguo all'interno della matrice.

Nell'esempio seguente viene creata una sezione dei cinque elementi centrali di una matrice integer a 10 elementi. Si noti che il codice raddoppia i valori di ogni numero intero nella sezione. Come illustrato nell'output, le modifiche apportate dall'intervallo vengono riflesse nei valori della matrice.

using System;

var array = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
var slice = new Span<int>(array, 2, 5);
for (int ctr = 0; ctr < slice.Length; ctr++)
    slice[ctr] *= 2;

// Examine the original array values.
foreach (var value in array)
    Console.Write($"{value}  ");
Console.WriteLine();

// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20
module Program

open System

[<EntryPoint>]
let main _ =
    let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
    let slice = Span<int>(array, 2, 5)
    for i = 0 to slice.Length - 1 do
        slice[i] <- slice[i] * 2

    // Examine the original array values.
    for value in array do
        printf $"{value}  "
    printfn ""
    0
// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20

Sezioni

Span<T> include due overload del Slice metodo che formano una sezione dall'intervallo corrente che inizia in corrispondenza di un indice specificato. In questo modo è possibile considerare i dati in come Span<T> un set di blocchi logici che possono essere elaborati in base alle esigenze da parti di una pipeline di elaborazione dati con un impatto minimo sulle prestazioni. Ad esempio, poiché i protocolli server moderni sono spesso basati su testo, la manipolazione di stringhe e sottostringhe è particolarmente importante. Nella classe il metodo principale per l'estrazione String di sottostringhe è Substring. Per le pipeline di dati che si basano su una manipolazione estesa delle stringhe, l'uso di offre alcune penalità per le prestazioni, poiché:

  1. Crea una nuova stringa per contenere la sottostringa.
  2. Copia un subset dei caratteri dalla stringa originale alla nuova stringa.

Questa operazione di allocazione e copia può essere eliminata usando Span<T> o ReadOnlySpan<T>, come illustrato nell'esempio seguente:

using System;

class Program2
{
    static void Run()
    {
        string contentLength = "Content-Length: 132";
        var length = GetContentLength(contentLength.ToCharArray());
        Console.WriteLine($"Content length: {length}");
    }

    private static int GetContentLength(ReadOnlySpan<char> span)
    {
        var slice = span.Slice(16);
        return int.Parse(slice);
    }
}
// Output:
//      Content length: 132
module Program2

open System

let getContentLength (span: ReadOnlySpan<char>) =
    let slice = span.Slice 16
    Int32.Parse slice

let contentLength = "Content-Length: 132"
let length = getContentLength (contentLength.ToCharArray())
printfn $"Content length: {length}"
// Output:
//      Content length: 132